#pragma once

#include <iostream>
#include <sys/types.h>
#include <sys/sem.h>
#include <unistd.h>
#include <cstdio>
#include <sys/ipc.h>
#include <memory>

const std::string pathname = "/tmp";
int proj_id = 0x77;

#define GET_SEM IPC_CREAT
#define BUILD_SEM (IPC_CREAT | IPC_EXCL | 0666)

class Semaaphore
{
public:
    Semaaphore(int semid, int flag) : _semid(semid), _flag(flag)
    {
    }

    ~Semaaphore()
    {
        if (_flag == GET_SEM)
            return;
        int n = semctl(_semid, 0, IPC_RMID);
        (void)n;
        std::cout << "sem destory" << std::endl;
    }

    void P()
    {
        struct sembuf s;
        s.sem_op = -1;
        s.sem_num = 0;
        s.sem_flg = SEM_UNDO;
        int n = semop(_semid, &s, 1);
        (void)n;
    }

    void V()
    {
        struct sembuf s;
        s.sem_op = 1;
        s.sem_num = 0;
        s.sem_flg = SEM_UNDO;
        int n = semop(_semid, &s, 1);
        (void)n;
    }

private:
    int _semid;
    int _flag;
};

class SemaphoreBulider
{

public:
    SemaphoreBulider() : _val(-1)
    {
    }

    ~SemaphoreBulider()
    {
    }

    SemaphoreBulider &SetVal(int val)
    {
        _val = val;
        return *this;
    }

    std::shared_ptr<Semaaphore> Build(int flag)
    {
        if (_val < 0)
        {
            std::cerr << "you must init val" << std::endl;
            return nullptr;
        }
        key_t key = ftok(pathname.c_str(), proj_id);
        if (key < 0)
        {
            std::cerr << "ftok cerror" << std::endl;
            exit(1);
        }
        int semid = semget(key, 1, flag);
        if (semid < 0)
        {
            std::cerr << "semget cerror" << std::endl;
            exit(1);
        }
        if (flag == BUILD_SEM)
        {
            union semum
            {
                int val;
                struct semid_ds *buf;
                unsigned short *array;
                struct seminfo *_buf;
            } un;

            un.val = _val;
            int n = semctl(semid, 0, SETVAL, un);
            if (n < 0)
            {
                std::cerr << "semctl cerror" << std::endl;
                exit(2);
            }
        }
        return std::make_shared<Semaaphore>(semid, _val);
    }

private:
    int _val;
};
